﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Audio;

namespace XwingGame
{
    public class Xwing
    {
        private XwingGame game;
        private Model model;
        private int wing1; // Index to the wing 1 bone
        private int wing2; // Index to the wing 2 bone
        private float wingAngle = 0;
        private bool deployed = false;
        private int laserNext = 0;
        private LaserFire laserFire;
        float laserCooldown = 5;
        private Cue engineSound = null;

        /// <summary>
        /// Ship orientation as a quaternion
        /// </summary>
        Quaternion orientation = Quaternion.Identity;
        

        /// <summary>
        /// Current position
        /// </summary>
        private Vector3 position = Vector3.Zero;

        /// <summary>
        /// How fast we are going (cm/sec)
        /// </summary>
        private float speed = 0;

        /// <summary>
        /// Thrust in cm/sec^2
        /// </summary>
        private float thrust;

        /// <summary>
        /// Decelleration due to drag
        /// </summary>
        private float drag = 0.5f;

        /// <summary>
        /// Maximum thrust (cm/sec^2)
        /// </summary>
        private const float MaxThrust = 2940;

        /// <summary>
        /// The current turning rate in radians per scond
        /// Effectively the asimuth change rate
        /// </summary>
        private float turnRate = 0;

        /// <summary>
        /// The maximum turning rate
        /// </summary>
        private const float MaxTurnRate = (float)Math.PI;

        /// <summary>
        /// Elevation change rate
        /// </summary>
        private float pitchRate = 0;

        /// <summary>
        /// The max elevation change rate
        /// </summary>
        private const float MaxPitchRate = (float)Math.PI / 2;

        /// <summary>
        /// Current Bank Angle in rads
        /// </summary>
        private float rollAngle = 0;

        /// <summary>
        /// Current Bank Speed in rads/sec
        /// </summary>
        private float rollSpeed = 0;

        /// <summary>
        /// Max Bank Speed in rads/sec
        /// </summary>
        private const float MaxRollSpeed = (float)Math.PI / 2;

        /// <summary>
        /// Array of the laser positions.
        /// </summary>
        private Vector3[] laserLocs = { new Vector3(-492.673f, 164.817f, 376.025f),
            new Vector3(492.217f, -142.531f, 379.140f),
            new Vector3(-492.673f, -142.626f, 379.140f),
            new Vector3(492.217f, 164.842f, 375.952f)};

        public Model Model { get { return model; } }
        public float TurnRate { get { return turnRate; } set { turnRate = value; } }
        public bool Deployed { get { return deployed; } set { deployed = value; } }
        public float Thrust { get { return thrust; } set { thrust = value; } }
        public Vector3 Position { get { return position; } set { position = value; } }
        public float PitchRate { get { return pitchRate; } set { pitchRate = value; } }
        public float RollSpeed { get { return rollSpeed; } set { rollSpeed = value; } }
        public float RollAngle { get { return rollAngle; } set { rollAngle = value; } }
        public Matrix Transform
        {
            get
            {
                return
                    Matrix.CreateFromQuaternion(orientation) *
                    Matrix.CreateTranslation(position);
            }
        }

        public LaserFire LaserFire { get { return laserFire; } }
        public float Speed { get { return speed; } set { speed = value; } }


        public Xwing(XwingGame game)
        {
            this.game = game;
            laserFire = new LaserFire(game);
        }

        /// <summary>
        /// This funcion is called to load content into this component
        /// of our game.
        /// </summary>
        /// <param name="content"> The content manager to load from. </param>
        public void LoadContent(ContentManager content)
        {
            model = content.Load<Model>("xwing");
            wing1 = model.Bones.IndexOf(model.Bones["Wing1"]);
            wing2 = model.Bones.IndexOf(model.Bones["Wing2"]);
            laserFire.LoadContent(content);
        }


        /// <summary>
        /// This function is called to update this component of our game
        /// to the current game time.
        /// </summary>
        /// <param name="gameTime"> current game time </param>
        public void Update(GameTime gameTime)
        {
            float wingDeployTime = 2.0f;
            float delta = (float)gameTime.ElapsedGameTime.TotalSeconds;
            if (deployed && wingAngle < 2.0f)
            {
                drag = 1;
                wingAngle += (float)(0.20 * delta / wingDeployTime);
                if(wingAngle > 0.20f)
                    wingAngle = 0.20f;
            }
            else if (!deployed && wingAngle > 0)
            {
                wingAngle -= (float)(0.20 * delta / wingDeployTime);
                if (wingAngle < 0)
                {
                    drag = 0.5f;
                    wingAngle = 0;
                }
            }

            if (thrust > 0)
            {
                if (engineSound == null)
                {
                    engineSound = game.SoundBank.GetCue("engine_2");
                    engineSound.Play();
                }
                else if (engineSound.IsPaused)
                {
                    engineSound.Resume();
                }
            }
            else
            {
                if (engineSound != null && engineSound.IsPlaying)
                {
                    engineSound.Pause();    
                }
            }

            //
            // Orientation updates
            //

            
            orientation *= Quaternion.CreateFromAxisAngle(new Vector3(0, 1, 0), turnRate * MaxTurnRate * delta);
            orientation *= Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), pitchRate * MaxPitchRate * delta);
            orientation *= Quaternion.CreateFromAxisAngle(new Vector3(0, 0, 1), rollSpeed * MaxRollSpeed * delta);
            orientation.Normalize();

            //
            // Position updates
            //

            float acceleration = thrust * MaxThrust - drag * speed;
            speed += acceleration * (float)gameTime.ElapsedGameTime.TotalSeconds;

            Matrix transform = Matrix.CreateFromQuaternion(orientation);

            Vector3 directedThrust = Vector3.TransformNormal(new Vector3(0, 0, 1), transform);
            position += directedThrust * speed * (float)gameTime.ElapsedGameTime.TotalSeconds;




            if (laserCooldown < 0.25f)
                laserCooldown += (float)gameTime.ElapsedGameTime.TotalSeconds;  
            laserFire.Update(gameTime);


        }


        /// <summary>
        /// This function is called to draw this game component.
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="gameTIme"><</param>
        public void Draw(GraphicsDeviceManager graphics, GameTime gameTime)
        {

            DrawModel(graphics, model, Transform);
            laserFire.Draw(graphics, gameTime);
        }

        private void DrawModel(GraphicsDeviceManager graphics, Model model, Matrix world)
        {
            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);

            transforms[wing1] = Matrix.CreateRotationY(wingAngle) * transforms[wing1];
            transforms[wing2] = Matrix.CreateRotationY(-wingAngle) * transforms[wing2];

            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.World = transforms[mesh.ParentBone.Index] * world;
                    effect.View = game.Camera.View;
                    effect.Projection = game.Camera.Projection;
                    
                }
                mesh.Draw();
            }
        }



        public void levelPlane(GameTime gameTime)
        {
            if (rollAngle > 0)
            {

                rollAngle -= MaxRollSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;

                if(rollAngle < 0 )
                    rollAngle = 0;
            }

            if (rollAngle < 0)
            {
                rollAngle += MaxRollSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;

                if (rollAngle > 0)
                    rollAngle = 0;
            }
        }

        public void FireLaser()
        {
            if (wingAngle < 0.2f || laserCooldown < 0.25f)
                return;
            laserCooldown = 0;

            Vector3 position = Vector3.Transform(laserLocs[laserNext], Transform);
            Matrix orientation = Transform;
            orientation.Translation = Vector3.Zero;

            Vector3 direction = Vector3.TransformNormal(new Vector3(0, 0, 1), orientation);

            laserFire.FireLaser(position, orientation);

            game.SoundBank.PlayCue("tx0_fire1");

            laserNext = (laserNext + 1) % 4;
        }


        public void Reset()
        {
            //reset all the necesarry privaet variables:
            wingAngle = 0;
            drag = 0.5f;
            deployed = false;
            laserNext = 0;
            laserCooldown = 5;
            rollAngle = 0;
            speed = 0;
            position = Vector3.Zero;
            orientation = Quaternion.Identity;
            LaserFire.clearList();
        }

    }
}
